#ifndef WIDGET_HPP
#define WIDGET_HPP

/********************************************************************
	created:	2011/01/16
	filename: 	E:\C++\Red\src\Graphics\Gui\Widget.hpp
	author:		Red
	
	purpose:	Base Widget. Can be drawn, have widget children etc
*********************************************************************/

#include "Engine/Config.hpp"
#include "Engine/RedSFML.hpp"
#include "SFML/Graphics/Drawable.hpp"

namespace red {
	namespace ui {
		class Widget;

		/// List of pointers on Widget
		typedef std::vector<Widget*>	WidgetList;

		/// Alignment type of a Widget in its parent
		enum WidgetAlignment{
			Left,
			Top,
			Right,
			Bottom,
			Center,

			TopLeft,
			TopRight,

			BottomLeft,
			BottomRight,

			Absolute
		};

		/// Parameter type of a widget.
		enum WidgetParameter{
			Position,
			Size,
			Focus
		};

		/// Depth indicator for widgets
		enum WidgetDepth{
			AboveAll,
			BelowAll,
			Down,
			Up
		};

		/// Base object in a GUI. All other objects are inheriting a Widget
		/// All widgets are part of a tree. Each has a parent and potentially children
		class REDAPI Widget : public sf::Drawable {
			friend class Gui;
		public:
			/// Constructor
			/// @param pPosition : Position TopLeft of widget on screen
			/// @param pSize	 : Size of widget on screen
			Widget(const Vector2F &pPosition = Vector2F::ZERO, const Vector2F &pSize = Vector2F(30,20));

			virtual ~Widget();

			/// Add a Widget as a child to this one
			/// @param pWidget : Widget to add as a child
			void Add(Widget &pWidget);

			/// Remove a child if it exists
			/// @param pWidget : Widget to remove
			void Remove(Widget* pWidget);

			/// Change the Widget depth
			/// @param pParam : parameter indicating new depth wanted
			void ChangeDepth(WidgetDepth pParam);

			/// Update the widget border from the widget size
			void UpdateBorder();

			// Getters
				/// Returns the bounding Rectangle of the widget
				Rectangle GetRectangle() const { return mRectangle; }

				/// Returns the size of the widget
				Vector2F GetSize() const { return mRectangle.Size(); }

				/// Returns the position of the widget
				Vector2F GetPosition() const { return SFMLToRedVector2F(Drawable::GetPosition()); }

				/// Returns true if the widget is visible
				bool IsVisible() const { return mVisible; }

				/// Returns true if the widget is focusable
				bool IsFocusable() const { return mFocusable; }

				/// Returns the currenty used alignment
				WidgetAlignment GetAlignment() const { return mAlignment; }

				/// Returns true if the widget use a border
				bool UseBorder() const { return mUseBorder; }

			// SetSize
				/// Sets the widget Position
				void SetPosition(const Vector2F &pPos) { mRectangle.SetPosition(pPos);
														 Drawable::SetPosition(RedToSFMLVector2F(pPos)); }

				/// Sets the widget Size
				void SetSize(const Vector2F &pSize) { mRectangle.SetSize(pSize);
													  OnUpdate(Size);	}

				/// Sets the widget width only
				void SetWidth(f32 pWidth) { mRectangle.SetWidth(pWidth);
											OnUpdate(Size);	}

				/// Sets the widget height only
				void SetHeight(f32 pHeight) { mRectangle.SetHeight(pHeight);
											  OnUpdate(Size);	}

				/// Change the widget alignment
				/// @param pAlignment : New alignment for the widget
				/// @param pParentOffset : Position offset from the parent. Optionnal
				void SetAlignment(WidgetAlignment pAlignment, const Vector2F &pParentOffset = Vector2F::ZERO);

				/// Change the widget alignment corresponding to the given string
				/// @param pAlignment : string containing a lowercase alignment
				/// @param pParentOffset : Position offset from the parent. Optionnal
				/// @return : True if a correct alignment has been set. Else, false.
				bool SetAlignmentFromString(const std::string &pAlignment, const Vector2F &pParentOffset = Vector2F::ZERO);

				/// Sets the parent offset of the widget
				/// @param pOffset : new offset
				void SetParentOffset(const Vector2F &pOffset) { mParentOffset = pOffset; }

				/// Make the widget use or not a border
				/// @param pVal : Boolean to activate or not the border
				/// @param pWidth : Border width, optionnal
				/// @param pColor : Border color, optionnal
				void UseBorder(bool pVal, f32 pWidth = 0, const Color &pColor = Color::Black);

				/// Sets the Border width
				void SetBorderWidth(f32 pWidth) { mBorderWidth = pWidth;
												  UpdateBorder();	   }

				/// Sets the Border color of the widget
				void SetBorderColor(const Color &pColor) { mBorderColor = pColor; }



		protected:
			/// Drawable overload of Render function
			virtual void Render(sf::RenderTarget &pTarget, sf::Renderer &pRenderer) const;

			/// Overloadable function drawing the widget
			virtual void OnRender(sf::RenderTarget &pTarget, sf::Renderer &pRenderer) const;

			/// Overloadable function called when a widget parameter has been modified
			/// @param pParam : The updated parameter
			virtual void OnUpdate(WidgetParameter pParam);

			Widget*				mParent;			///< Parent of this widget
			WidgetList			mChildren;			///< Children of this widget

			Rectangle			mRectangle;			///< Bounding rectangle of widget
			WidgetAlignment		mAlignment;			///< Widget alignment 
			Vector2F			mParentOffset;		///< Position offset from parent (if Alignment)

			Rectangle			mBorderRectangle;	///< Bounding rectangle of the widget inside the border
			bool				mUseBorder;			///< True if a border must be drawn
			f32					mBorderWidth;		///< Border width
			Color				mBorderColor;		///< Border Color of the widget

			bool				mEnabled;			///< Is Widget renderable
			bool				mVisible;			///< Is Widget visible
			bool				mFocusable;			///< Is Widget focusable

			/// Render all direct children
			void RenderChildren(sf::RenderTarget &pTarget, sf::Renderer &pRenderer) const;

			/// Find a widget in this widget children
			WidgetList::iterator FindChild(const Widget* pChild);

			/// Update the widget alignment/position
			void CalculatePosition();

			/// Draw a widget border with "OpenGL" calls
			void DrawBorder(sf::Renderer &pRenderer) const;
		};
	}
}
#endif // WIDGET_HPP
